#include "PageCache.h"

PageCache PageCache::_inst;

PageCache& PageCache::getInstance()
{
    return _inst;
}

Span *PageCache::newSpan(size_t nPage)
{
    // 如果当前pageCache里面有需要的页数的span，就直接返回
    if(!_pageSpanList[nPage].empty()){
        Span *retSpan = _pageSpanList[nPage].pop();


        // 如果当前span的页数足够使用，那么就返回当前span，并添加到map映射中
        for(size_t k = 0; k < retSpan->_n; k++){
            _pageIdSpanMap[retSpan->_page_id + k] = retSpan;
        }
    }

    // 没有刚好符合大小的span，所以遍历比他大的，将大的span切分为 k 和 span->_n - k 大小的两个span
    size_t k = nPage + 1;
    while(k <= NPAGES - 1){
        if(!_pageSpanList[k].empty()){
           Span *nSpan = _pageSpanList[k].pop();
           Span *kSpan = new Span;

           kSpan->_page_id = nSpan->_page_id;
           nSpan->_page_id += nPage;

           kSpan->_n = nPage;
           nSpan->_n -= nPage; 

           // 将切分后的span挂载到符合的大小上，需要的span返回
           _pageSpanList[nSpan->_n].pushFront(nSpan);

           // 将nspan的第一页和最后一页添加到map映射中，便于合并span
           _pageIdSpanMap[nSpan->_page_id] = nSpan;
           _pageIdSpanMap[nSpan->_page_id + nSpan->_n - 1] = nSpan;

           // 将且分出的kSpan所有页，添加到映射中
           for(size_t k = 0; k < kSpan->_n; k++){
            _pageIdSpanMap[kSpan->_page_id + k] = kSpan;
           }

           return kSpan;
        }
        k++;
    }

    // 如果最大的 128 页的span也没有，那么就到堆中去申请
    void *ptr = systemAlloc(NPAGES - 1);
    
    Span *bigSpan = new Span;
    bigSpan->_n = NPAGES - 1;
    bigSpan->_page_id = (PAGE_ID)ptr >> PAGE_SHIFT;
    _pageSpanList[NPAGES - 1].pushFront(bigSpan);

    return newSpan(nPage);
}

mutex &PageCache::getMtx()
{
    return _mtx;
}

Span *PageCache::pageIdSpanMap(PAGE_ID pageId)
{
    unordered_map<PAGE_ID, Span *>::iterator iter = _pageIdSpanMap.find(pageId);
    
    if(iter != _pageIdSpanMap.end()){
        return iter->second;
    }else{
        return nullptr;
    }

}

void PageCache::dealloc(Span *span)
{
    // 找到当前span的前一个页号和后一个页号
    while(1){
        Span *preSpan = pageIdSpanMap(span->_page_id - 1);
        // 如果当前span没有前一页或者前一页还在centralCache中使用，或者两个span加一起大于128，就不符合
        if(!preSpan || preSpan->_isUse || preSpan->_n + span->_n > NPAGES - 1){
            break;
        }

        span->_page_id = preSpan->_page_id;
        span->_n += preSpan->_n;
        // 将前一页span 从spanlist中移除
        _pageSpanList[preSpan->_n].erase(preSpan);

        delete preSpan;
    }

    while(1){
        Span *nextSpan = pageIdSpanMap(span->_page_id + span->_n);
        if(!nextSpan || nextSpan->_isUse || nextSpan->_n + span->_n > NPAGES - 1){
            break;
        }

        span->_n += nextSpan->_n;
        _pageSpanList[nextSpan->_n].erase(nextSpan);

        delete nextSpan;
    }

    span->_isUse = false;
    _pageSpanList[span->_n].pushFront(span);
    _pageIdSpanMap[span->_page_id] = span;
    _pageIdSpanMap[span->_page_id + span->_n - 1] = span;
    
}